#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'walkingsky'

import requests
import random
#import json
import re
import demjson
import datetime

class FundService:

    def __init__(self):
        self.headers={"User-Agent" : "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) ",
            "Accept" : "*/*",
            "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
            "Connection" : "keep-alive",
            #"Referer" : refer_url,
            #"Cookie" : "ASP.NET_SessionId=fn33e1v1horkjpvmjdsdtrnu",
            "Accept-Encoding":"gzip, deflate",
            "Host" : "fund.eastmoney.com",
        }
        pass       
        
    
    def get_found_by_code(self,code):
        '''
            获取基金数值
            #code 参数，可以是一个单code，也可以是逗号分隔的多个code
        '''
        if code == '':
            print('基金code为空')
            return False
        base_url = 'https://fundmobapi.eastmoney.com/FundMNewApi/FundMNFInfo?'
        #pageIndex=1&pageSize=500&appType=ttjj&plat=Android&product=EFund&Version=1&deviceid=4252d0ac69bb50&Fcodes=006751,006752
        param_deviceid = str(random.random()*10000000000)
        url = base_url+'pageIndex=1&pageSize=500&appType=ttjj&plat=Android&product=EFund&Version=1&deviceid='+param_deviceid+'&'
        res = requests.get(url+'Fcodes='+code)
        if(res.status_code == 200):
            json_obj = res.json()
            if (json_obj.get('ErrCode') == 0 ):
                for i in json_obj.get('Datas'):
                    print(i.get('FCODE')+':'+i.get('SHORTNAME')+':'+i.get('GSZ')+':'+i.get('GZTIME')+':')
                return json_obj.get('Datas')
            else:
                print('[get_found_by_code]获取基金数值：返回错误：'+str(json_obj.get('ErrCode')))
                return False
        else:
            return False

        print(res.status_code)
        print(res.text)

    
    def get_history_by_code(self,code):
        '''
        基金历史数据接口
        '''
        
        url = 'https://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code=' + code + '&page=1&per=49'
        res = requests.get(url)
        if(res.status_code == 200):
            pages = int( re.findall(r",pages:(\d+),",res.text)[0])        
        
        str_res = ''

        for i in range(1,pages+1,1):
            url = 'https://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code=' + code + '&page='+ str(i) +'&per=49'
            res = requests.get(url)
            if(res.status_code == 200):
                pages = int( re.findall(r",pages:(\d+),",res.text)[0])
                str_res = str_res + res.text
        #print('{"datas":['+self.resove_history_data(str_res)+']}')
        return('{"datas":['+self.resove_history_data(str_res)+']}')
       

    def resove_history_data(self,datas):
        re_datas = re.findall(r"<tr>(?:(?!</tr>).)*</tr>",datas)
        return_data = ''
        for re_data in re_datas:
            #print(re.findall(r"<td[^>]*>((?:(?!</td>).)*)</td>",re_data))
            data = re.findall(r"<td[^>]*>((?:(?!</td>).)*)</td>",re_data)
            if len(data) >1:
                return_data = return_data +'{"date":"'+data[0]+'","dwjz":"'+data[1]+'","ljjz":"'+data[2]+'","rzzl":"'+data[3]+'"},'
        return return_data[:-1]


    def get_fundranking(self,kind:str='all',duration:str='1nzf',
                        num:int=100,start_time:str=None,end_time:str=None,
                        page:int=1,get_all:bool=False,dx:str='1',is_etf:bool=False):        
        '''
        获取基金排行

        args:
            is_etf:bool 是否是场内基金，默认False
            kind: 基金类型
                    all:全部
                    gp:股票型基金
                    zs:指数型
                    hh:混合型
                    zq:债券型
                    qdii: QDII
                    fof: FOF

            duration :时长：rzzf 日增长幅
                        zzf 一周的增幅
                        1yzf 一月的增幅
                        3yzf 三个月的增幅
                        6yzf 六个月的增幅
                        1nzf 1年的增幅
                        2nzf 2年的增幅
                        3nzf 3年的增幅
                        jnzf 今年以来的增幅
                        lnzf 成立以来的增幅
            num ：取前num名（最大10000）
            start_time : 起始时间，
            end_time :  终止时间
            page: 页码 默认1
            get_all:bool  是否获取所有数据
            dx:str #dx=1 可购买基金 ;dx=0 全部基金
        返回：
            一个list变量
            [   "019035,易方达全球医药行业混合发起式(QDII)C(人民币),YFDQQYYHYHHFQSQDIICRMB,2025-07-17,1.3201,1.3201,5.57,13.95,20.82,54.56,93.22,83.27,,,85.07,48.51,2023-11-15,1,,,0.00%,,,,", 
                "008284,易方达全球医药行业混合发起式(QDII)A(人民币),YFDQQYYHYHHFQSQDIIARMB,2025-07-17,1.3305,1.3305,5.56,13.93,20.68,54.14,92.83,83.37,54.14,55.81,84.74,33.05,2020-01-20,1,,1.50%,0.15%,1,0.15%,1,3.04"
                ...
                ]
        '''
        import random

        if end_time is None:
            end_time = datetime.date.today()
            end_time_str = end_time.strftime('%Y-%m-%d')
        else:
            end_time_str = end_time

        if start_time is None:
            delta = datetime.timedelta(days=365)
            start_time = end_time - delta 
            start_time_str = start_time.strftime('%Y-%m-%d')
        else:
            start_time_str = start_time

        v = ''.join(random.choices('0123456789', k=17))

        if is_etf:
            url = 'https://fund.eastmoney.com/data/rankhandler.aspx?op=ph&dt=fb&ft=ct&rs=&gs=0&sc=' + duration + \
                '&st=desc&pi=' + str(page) + \
                '&pn='+str(num)+'&v=0.'+v
        else:

            #dx=1 可购买基金 ;dx=0 全部基金
            url = 'https://fund.eastmoney.com/data/rankhandler.aspx?op=ph&dt=kf&ft=' + kind + '&rs=&gs=0&sc=' + duration + \
                '&st=desc&sd=' + start_time_str + '&ed=' + end_time_str + \
                '&qdii=&tabSubtype=,,,,,&pi=' + str(page) + \
                '&pn='+str(num)+'&dx=' + str(dx) + '&v=0.'+v
        refer_url = 'https://fund.eastmoney.com/data/fundranking.html?spm=100015.lw.2'
        headers = self.headers
        headers["Referer"] = refer_url
        #print(url)
        try: 
            res = requests.get(url,headers=headers)
            if(res.status_code == 200):
                res = re.findall('(\{[^\}]+\})',res.text)
                # print(res[0])
                res_json = demjson.decode( res[0],encoding='utf-8')
                result_list = res_json.get('datas',[])
                # 判断是否需要连续获取其他分页内容
                if get_all and res_json.get('pageIndex',1) < res_json.get('allPages',2):
                    page = page + 1
                    page_data = self.get_fundranking(is_etf=is_etf,kind=kind,duration=duration,num=num,
                                                     start_time=start_time_str,end_time=end_time_str,page=page,get_all=get_all)
                    # 合并数据
                    result_list = result_list + page_data
                    
                return result_list
                '''
                for i in res_json.get('datas'):
                    data = i.split(',')
                    print(data[0]+':'+data[1]+':'+data[2]+':'+data[3]+':'+data[4]+':'+data[5])
                    self.get_position(data[0])
                '''
            else:
                return []
        except Exception as e:
            print(e)
            return []
                
            
            

    def get_position(self,fund_code):
        '''
        获取基金的股票持仓情况
        args:
            fund_code 基金代码
        '''

        url = 'https://fund.eastmoney.com/'+fund_code+'.html'
        res = requests.get(url)
        #print(res.text)
        res.encoding='utf-8'
        #res = re.findall('href="http://quote\.eastmoney\.com/([a-z\d]+)\.html" title="([^"]+)"',res.text)
        res = re.findall('href="//quote\.eastmoney\.com/unify/r/([a-z\d\.]+)" title="([^"]+)"',res.text)
        return res
        for i in res:
            print(i[0]+i[1])
   
    def get_ljsyzs(self,session:requests.Session,fund_code:str,index_code:str='000300',duration_type:str='hy'):
        '''
            获取基金的历史收益走势数据
            参数:
            fund_code:str  基金代码
            index_code:str 对比指数类型 默认沪深300 
            duration_type:str 时长 默认 6个月
        '''
        from config.data_config import FUND_INDEXCODE_ARR,FUND_DURATION_TYPE_ARR
        # 可对比的指数类型 沪深300 ，中证500，上证指数，深证指数，中小板指，创业板指
        indexcode_array = list(FUND_INDEXCODE_ARR.keys())
        # 时长类型    1个月，3个月，6个月，1年，3年，5年，今年以来，成立以来  
        type_array = list(FUND_DURATION_TYPE_ARR.keys())

        if index_code in indexcode_array and duration_type in type_array:
            import time,random

            # 获取当前时间的Unix时间戳（浮点数，单位：秒）
            timestamp_seconds_float = time.time()
            # 转换为毫秒级（乘以1000并取整）
            timestamp_milliseconds = int(timestamp_seconds_float * 1000)
            timestamp_ms_str = str(timestamp_milliseconds)

            # 生成17位随机数字符串
            random_str = ''.join(random.choices('0123456789', k=17))
            callback_str = 'jQuery1830' + random_str + '_'+timestamp_ms_str

            url = 'https://api.fund.eastmoney.com/pinzhong/LJSYLZS' \
                    '?fundCode=' + fund_code +\
                    '&indexcode=' + index_code +\
                    '&type=' + duration_type + \
                    '&callback=' + callback_str + \
                    '&_=' + timestamp_ms_str
            
            
            try:
                res = session.get(url,timeout=(30,60))
                
                if(res.status_code == 200):
                    #res = re.findall('\(([^\)]+)\)',res.text)
                    res_str = res.text[42:-1]
                    # print(res_str)
                    res_json = demjson.decode( res_str,encoding='utf-8')
                    if res_json.get('ErrCode') == 0:
                        result_list = res_json.get('Data',[])
                        return result_list
                    else:
                        return []
            except requests.exceptions.RequestException as e:
                print(e)
                return []
        else:
            print('输入参数不正确')
            return []

    def get_lsjz(self,session:requests.Session,fund_code:str, start_date:str=None):
        """
        获取基金历史净值数据
        
        参数:
            fund_code:str 基金代码
            start_date:str 开始日期，格式为"YYYY-MM-DD"，如果为None则获取所有历史数据
        
        返回:
            list: 包含所有符合条件的基金净值数据的列表
        """
        import time,json

        # 初始化结果列表
        result = []
        
        # 初始化页码
        page_index = 1
        page_size = 20  # 每页获取20条数据
        
        while True:
            # 生成时间戳和随机字符串
            timestamp_seconds_float = time.time()
            timestamp_milliseconds = int(timestamp_seconds_float * 1000)
            timestamp_ms_str = str(timestamp_milliseconds)
            random_str = ''.join(random.choices('0123456789', k=17))
            callback_str = 'jQuery1830' + random_str + '_' + timestamp_ms_str
            
            # 构建URL
            url = f'https://api.fund.eastmoney.com/f10/lsjz' \
                f'?fundCode={fund_code}' \
                f'&pageIndex={page_index}' \
                f'&pageSize={page_size}' \
                f'&startDate={start_date if start_date else ""}' \
                f'&endDate=' \
                f'&callback={callback_str}' \
                f'&_={timestamp_ms_str}'
            
            
            try:
                # 发送请求
                res = session.get(url, timeout=(30,60))
                
                if res.status_code != 200:
                    print(f"请求失败，状态码: {res.status_code}")
                    break
                
                # 处理JSONP响应，提取JSON数据
                json_str = re.sub(r'^jQuery.*?\(', '', res.text)
                json_str = re.sub(r'\)$', '', json_str)
                
                # 解析JSON
                data = json.loads(json_str)
                
                # 检查是否有数据
                if not data.get("Data") or not data["Data"].get("LSJZList"):
                    print(f"基金 {fund_code} 第{page_index}页没有更多数据")
                    break
                
                # 获取当前页的列表数据
                current_page_data = data["Data"]["LSJZList"]
                
                # 如果设置了开始日期，检查是否有数据早于或等于开始日期
                if start_date:
                    # 过滤出日期晚于等于start_date的数据
                    filtered_data = [item for item in current_page_data if item["FSRQ"] >= start_date]
                    result.extend(filtered_data)
                    
                    # 如果当前页有数据早于start_date，说明已经获取了所有需要的数据
                    if len(filtered_data) < len(current_page_data):
                        break
                else:
                    # 如果没有设置开始日期，获取所有数据
                    result.extend(current_page_data)
                
                # 检查是否已经获取了所有数据
                total_count = data.get("TotalCount", 0)
                if len(result) >= total_count:
                    print(f"基金 {fund_code} 已获取全部数据，共{len(result)}条")
                    break
                
                # 准备获取下一页
                page_index += 1
                print(f"基金 {fund_code} 正在获取第{page_index}页数据...")
                
                # 添加适当的延迟，避免请求过于频繁
                time.sleep(0.5)
                
            except requests.exceptions.RequestException as e:
                print(f"基金 {fund_code} 发生错误: {str(e)}")
                break
        # 保留的字段列表
        keep_keys = ["FSRQ", "DWJZ", "LJJZ"]

        # 过滤数据，只保留指定字段，简化数据
        filtered_data = [
            {key: item[key] for key in keep_keys}  # 只保留指定的3个键
            for item in result
        ]    
        return filtered_data
    
    def get_fund_info(self,session:requests.Session,fund_code:str,start_date:str=None):
        '''
            获取基金所有信息数据
            
            参数:
                fund_code:str 基金代码
                start_date:str 开始日期，格式为"YYYY-MM-DD"，如果为None则获取所有历史数据            
            返回:
                list: 包含所有符合条件的基金净值数据的列表
        '''
        import time,json

        # 生成随机时间戳（近30天内的随机时间）
        now = time.time()
        ten_min_ago = now - 3 * 60 * 60
        random_timestamp = random.uniform(ten_min_ago, now)
        
        # 转换为指定格式的字符串（例如：20250802125747）
        dt = datetime.datetime.fromtimestamp(random_timestamp)
        v_param = dt.strftime("%Y%m%d%H%M%S")
        
        # 构建请求URL
        url = f"https://fund.eastmoney.com/pingzhongdata/{fund_code}.js?v={v_param}"

        # 设置请求头
        headers = self.headers
        headers["Referer"] = f'https://fund.eastmoney.com/{fund_code}.html'
        headers["Host"] = 'fund.eastmoney.com'
        headers["User-Agent"] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'

        try:
            # 发送请求
            response = session.get(url, timeout=(30,60))
            
            # 检查响应状态
            response.raise_for_status()

            var_name = 'Data_netWorthTrend'
            # 使用正则匹配变量内容（处理数组和对象格式）
            pattern = re.compile(f"var {var_name} = (.*?);", re.DOTALL)
            match = pattern.search(response.text)
            if not match:
                print(f"未找到变量 {var_name}")

            var_a = match.group(1)

            var_name = 'Data_ACWorthTrend'
            # 使用正则匹配变量内容（处理数组和对象格式）
            pattern = re.compile(f"var {var_name} = (.*?);", re.DOTALL)
            match = pattern.search(response.text)
            if not match:
                print(f"未找到变量 {var_name}")

            var_b = match.group(1)

            net_worth_trend = json.loads(var_a)
            ac_worth_trend = json.loads(var_b) 

            if len(net_worth_trend)  != len(ac_worth_trend):
                print('数据未对其，需要特殊处理')


            # 3. 数据格式转换
            result = []
            for net_item, ac_item in zip(net_worth_trend, ac_worth_trend):
                # 提取时间戳进行比较
                timestamp_a = net_item['x']
                timestamp_b = ac_item[0]
                
                # 当时间戳相同时才合并
                if timestamp_a == timestamp_b:
                    # 转换时间戳为日期（毫秒级时间戳需除以1000）
                    timestamp = net_item["x"] / 1000
                    fsrq = datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")

                    # 跳过过早日期的数据
                    if start_date:
                        # 解析格式为YYYY-MM-DD的字符串
                        date_a = datetime.datetime.strptime(start_date, "%Y-%m-%d")
                        date_b = datetime.datetime.strptime(fsrq, "%Y-%m-%d")
                        if date_b < date_a:
                            continue
                    
                    # 提取单位净值和累计净值
                    dwjz = str(net_item["y"])
                    ljjz = str(ac_item[1])  # ACWorthTrend的第二个元素是累计净值
                    
                    result.append({
                        "FSRQ": fsrq,
                        "DWJZ": dwjz,
                        "LJJZ": ljjz
                    })
                else:
                    print(f"跳过不匹配的时间戳: a={timestamp_a}, b={timestamp_b}")

            return result
            
        except requests.exceptions.RequestException as e:
            print(f"获取基金信息失败: {str(e)}")
            return []
